package com.cmes.security;

import cn.dev33.satoken.SaManager;
import cn.dev33.satoken.config.SaTokenConfig;
import cn.dev33.satoken.context.SaHolder;
import cn.dev33.satoken.filter.SaServletFilter;
import cn.dev33.satoken.interceptor.SaInterceptor;
import cn.dev33.satoken.jwt.StpLogicJwtForSimple;
import cn.dev33.satoken.router.SaHttpMethod;
import cn.dev33.satoken.router.SaRouter;
import cn.dev33.satoken.stp.StpLogic;
import com.cmes.core.context.security.SecurityContextHolder;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Primary;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

/**
 * 权限模块自动启动
 *
 * @author Bruce.Gong
 * @since 1.0.0-SNAPSHOT
 */
@Slf4j
@ConditionalOnWebApplication
@ConditionalOnClass(SaManager.class)
public class SaAutoConfiguration implements WebMvcConfigurer {

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(new SaInterceptor(handle -> {
            SaRouter.match("/**")
                    .notMatch("/oauth/login")
                    .check(r -> StpAdminUtil.checkLogin())
                    .check(v -> {
                        SecurityContextHolder.setCurrentUser(
                                StpAdminUtil.getLoginType(),
                                StpAdminUtil.getLoginIdAsLong(),
                                (String) StpAdminUtil.getExtra("username"),
                                (String) StpAdminUtil.getExtra("nickname"));
                        SecurityContextHolder.setToken(StpAdminUtil.getTokenValue());
                    });

        })).addPathPatterns("/**");
    }

    /**
     * 只接受使用请求头进行传递 token
     */
    @Bean
    @Primary
    public SaTokenConfig saTokenConfig() {
        log.info("Initializing SaToken configuration");
        SaTokenConfig config = new SaTokenConfig();
        config.setTokenName("x-access-token");
        config.setTokenPrefix("Bearer");
        // token有效期，单位s
        config.setTimeout(30 * 24 * 60 * 60);
        // token临时有效期，单位s，（指定时间内有操作则自动续期）
        config.setAutoRenew(true);
        // 是否允许同一账号并发登录，为true时允许一起登录, 为false时新登录挤掉旧登录
        config.setIsConcurrent(true);
        // 在多人登录同一账号时，是否共用一个token，为true时所有登录共用一个token, 为false时每次登录新建一个token
        config.setIsShare(true);
        // 是否输出操作日志
        config.setIsLog(false);
        // 是否打印 SaToken 标记
        config.setIsPrint(false);
        // 阻止请求体携带 Token
        config.setIsReadBody(false);
        // 阻止 Cookie 携带 Token
        config.setIsReadCookie(false);
        config.setTokenSessionCheckLogin(false);
        config.setJwtSecretKey("qwfmbmjlgikmmdwdseaaqmkcldkemfjtnskwn");
        return config;
    }

    @Bean
    public SecurityListener securityListener() {
        return new SecurityListener();
    }

    @Bean
    public SecurityExceptionHandler securityExceptionHandler() {
        return new SecurityExceptionHandler();
    }

    @Bean
    public StpLogic getStpLogicJwt() {
        log.info("Initializing SaToken JWT with StpLogicJwtForStateless");
        StpLogicJwtForSimple jwt = new StpLogicJwtForSimple(StpAdminUtil.TYPE);
        StpAdminUtil.setStpLogic(jwt);
        return jwt;
    }

    @Bean
    public SaServletFilter getSaServletFilter() {
        return new SaServletFilter()
                .addInclude("/**")
                .addExclude("/favicon.ico")
                .setBeforeAuth(obj -> {
                    // ---------- 设置跨域响应头 ----------
                    SaHolder.getResponse()
                            // 允许指定域访问跨域资源
                            .setHeader("Access-Control-Allow-Origin", "*")
                            // 允许所有请求方式
                            .setHeader("Access-Control-Allow-Methods", "POST, GET, OPTIONS, DELETE")
                            // 有效时间
                            .setHeader("Access-Control-Max-Age", "3600")
                            // 允许的header参数
                            .setHeader("Access-Control-Allow-Headers", "*");

                    // 如果是预检请求，则立即返回到前端
                    SaRouter.match(SaHttpMethod.OPTIONS).back();
                });
    }

}
